// Copyright 2011 Jay Young. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

/**
 * @fileoverview
 *
 * A command object for posting new blog entries.
 */

goog.provide('rb.publish.PublishCommand');

goog.require('goog.crypt');
goog.require('goog.crypt.Sha1');
goog.require('goog.json');

goog.require('relief.rpc.Command');



/**
 * A command object for posting new blog entries.
 *
 * @param {function(string)} onSuccess On successful posting, the this callback
 *    will be passed the unique slug from the posted blog entry.
 * @param {function(rb.publish.PublishCommand.Response)} onFailure If there is
 *    an error, PublishCommand will call this function with the response code.
 * @param {rb.post.BlogPost} post The blog post to send.
 * @param {boolean=} isUpdate True if this request is to update an existing
 *    post, false or undefined for for creation of a new post.
 *
 * @constructor
 * @extends {relief.rpc.Command}
 */
rb.publish.PublishCommand = function(onSuccess, onFailure, post, isUpdate) {
  var hash;
  /**
   * @type {string}
   * @private
   */
  this.checksum_ = hash = this.hashPost_(post);

  goog.base(this, onSuccess, onFailure, 'PublishCommand:' + hash,
            '/api/admin/post/new', 'POST', 0); // No retries.

  /**
   * @type {rb.post.BlogPost}
   * @private
   */
  this.post_ = post;

  /**
   * @type {boolean}
   * @private
   */
  this.isUpdate_ = !!isUpdate;

  this.writeToCache = true;
};
goog.inherits(rb.publish.PublishCommand, relief.rpc.Command);


/**
 * Create a Sha1 checksum of the post contents.
 *
 * @param {rb.post.BlogPost} post The post to hash.
 * @return {string} The resulting 20-byte hexadecimal (uppercase) checksum.
 * @private
 */
rb.publish.PublishCommand.prototype.hashPost_ = function(post) {
  var sha = new goog.crypt.Sha1();
  sha.update(post.getContent());
  var hash = sha.digest();
  return goog.crypt.byteArrayToHex(hash).toUpperCase();
};


/**
 * @inheritDoc
 */
rb.publish.PublishCommand.prototype.getData = function() {
  var post = this.post_;

  return goog.json.serialize({
    'checksum': this.checksum_,
    'post': {
      'title': post.getTitle(),
      'author': post.getAuthor().userId,
      'content': post.getContent(),
      'created': post.getCreatedAsIso()
    },
    'isUpdate': this.isUpdate_
  });
};


/**
 * Possible responses from the server.
 * @enum {number}
 */
rb.publish.PublishCommand.Response = {
  /**
   * Post successfully posted.
   */
  SUCCESS: 0,

  /**
   * The server's calculated checksum did not match the one given.
   */
  CHECKSUM_MISMATCH: 1,

  /**
   * There was a datastore error while saving.
   */
  DATASTORE_ERROR: 2,

  /**
   * Posting as the wrong user.
   */
  AUTHENTICATION_ERROR: 3,

  /**
   * A non-admin is attempting to post a blog entry.
   */
  UNAUTHORIZED: 4,

  /**
   * An indeterminable error occurred during transmission.
   */
  UNKNOWN_ERROR: 5
};


/**
 * Callback for successful server requests.
 *
 * @param {goog.net.XhrManager.Event} event The COMPLETE event for the XHR Req.
 * @override
 */
rb.publish.PublishCommand.prototype.onSuccess = function(event) {
  var resp = event.target.getResponseJson(),
      status = resp['status'];

  if (status === rb.publish.PublishCommand.Response.SUCCESS) {
    var slug = resp['slug'];
    this.post_.setSlug(slug);
    this.callersOnSuccess(slug);
  }
  else {
    this.callersOnFailure(resp);
  }
};


/**
 * Callback for failed server responses.
 *
 * @param {goog.net.XhrManager.Event} event The COMPLETE event for the XHR Req.
 * @override
 */
rb.publish.PublishCommand.prototype.onFailure = function(event) {
  /** @preserveTry */
  try {
    var resp = event.target.getResponseJson();
  }
  catch (e) {
    this.callersOnFailure(rb.publish.PublishCommand.Response.UNKNOWN_ERROR);
    return;
  }

  this.callersOnFailure(resp['status']);
};


/**
 * @return {Array.<string>} The unique slug for this post, which is used as a
 * key name on the server.
 *
 * @override
 */
rb.publish.PublishCommand.prototype.getCacheKeys = function() {
  var key = this.post_.getCacheKey();
  if (key) {
    return [key];
  }
  else {
    return [];
  }
};


/**
 * @return {Array.<{key: string, value: rb.post.BlogPost}>} An array of cache
 *    key/value pairs.
 * @override
 */
rb.publish.PublishCommand.prototype.getCacheValues = function() {
  var post = this.post_;
  return [{
    key: post.getCacheKey(),
    value: post
  }];
};
